package cn.sciento.transfer.infra.datasource.mybatis;

import cn.sciento.core.exception.CommonException;import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import cn.sciento.mybatis.DynamicSqlMapper;
import cn.sciento.mybatis.impl.DefaultDynamicSqlMapper;
import cn.sciento.transfer.infra.datasource.factory.DataSourceContextHolder;
import cn.sciento.transfer.infra.datasource.provider.TransferDataSourceProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class DynamicSqlMapperTemplate {
  private static final Logger LOGGER = LoggerFactory.getLogger(DynamicSqlMapperTemplate.class);
  
  @Autowired
  private SqlSessionFactory sqlSessionFactory;
  
  public <T> T doExecute(String dataSourceCode, String serviceName, TransactionIsolationLevel level, DynamicSqlMapperExecution<T> dynamicSqlMapperExecution) {
    TransferDataSourceProvider.loadDataSource(dataSourceCode, serviceName);
    DataSourceContextHolder.setDataSourceLookupKey(TransferDataSourceProvider.getDataSourceKey(dataSourceCode));
    SqlSession sqlSession = (level == null) ? this.sqlSessionFactory.openSession() : this.sqlSessionFactory.openSession(level);
    DefaultDynamicSqlMapper defaultDynamicSqlMapper = new DefaultDynamicSqlMapper(sqlSession);
    try {
      T result = dynamicSqlMapperExecution.doExecuteInternal((DynamicSqlMapper)defaultDynamicSqlMapper);
      sqlSession.commit();
      return result;
    } catch (Exception ex) {
      sqlSession.rollback();
      sqlSession.close();
      DataSourceContextHolder.clearDataSourceLookupKey();
      LOGGER.error("execute dynamic sql mapper with error: ");
      throw new CommonException("execute dynamic sql mapper error", ex);
    } finally {
      sqlSession.close();
      DataSourceContextHolder.clearDataSourceLookupKey();
    } 
  }
  
  @FunctionalInterface
  public static interface DynamicSqlMapperExecution<T> {
    T doExecuteInternal(DynamicSqlMapper param1DynamicSqlMapper);
  }
}
